#ifndef MISC_UTILS_H
#define MISC_UTILS_H

#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <assert.h>

/*
 * A simple linked list.
 * Note: list->head->prev == NULL; list->tail->next == NULL.
 * There are two ways of using this list
 * 1. You can create a struct, and embed LIST_NODE_T as a field of this struct.
 * 2. Put whatever stuff in LIST_NODE_T::data
 */
typedef struct LIST_NODE_T
{
    struct LIST_NODE_T* prev;
    struct LIST_NODE_T* next;
    void* data; // this is not necessary if you use list in the first way
} LIST_NODE_T;

typedef struct LIST_T
{
    LIST_NODE_T* head;
    LIST_NODE_T* tail;
    int          size;
} LIST_T;

void list_init(LIST_T* list);
void list_add_head(LIST_T* list, LIST_NODE_T* node);
void list_add_tail(LIST_T* list, LIST_NODE_T* node);
void list_remove(LIST_T* list, LIST_NODE_T* node);
void list_remove_head(LIST_T* list);
void list_remove_tail(LIST_T* list);
void list_insert_after(LIST_T* list, LIST_NODE_T* node, LIST_NODE_T* prev);
void list_insert_before(LIST_T* list, LIST_NODE_T* node, LIST_NODE_T* next);

#define CONTAINER_PTR(type, field, ptr) (type*)((char*)(ptr) - offsetof(type, field))
#define NEW(type) (type*)malloc(sizeof(type))
#define DELETE(ptr) free(ptr)

/*
 * String functions
 */
typedef struct STRING_T
{
    int length;
    char data[1];
} STRING_T;

STRING_T* string_new(const char* str);
void string_destroy(STRING_T* str);
STRING_T* string_cat(const char* a, const char* b);
STRING_T* string_printf(const char* fmt, ...);
STRING_T* string_vprintf(const char* fmt, va_list vl);
STRING_T** string_split(const char* str, const char* delimit);  // returns an array, terminated by NULL
STRING_T* string_substr(const char* str, int start, int len);
int string_find(const char* str, const char* needle);
int string_equal(const char* a, const char* b);
STRING_T* string_trim(const char* str); // remove leading and trailing spaces

/*
 * Simple key-value file.
 */
typedef struct PROP_FILE_T PROP_FILE_T;

PROP_FILE_T* prop_parse(FILE* file);
void prop_destroy(PROP_FILE_T* prop);
STRING_T* prop_get(PROP_FILE_T* prop, const char* key); // do not free the string
STRING_T* prop_get_fmt(PROP_FILE_T* prop, const char* key_fmt, ...);

/*
 * Packet buffer. The buffer size grows as needed.
 * The elements in the packet buffer are stored in network byte order (Big Endian).
 */
typedef struct PACKET_BUFFER_T
{
    unsigned char* buffer;
    int buffer_length;
    int data_length;
    int index;
} PACKET_BUFFER_T;

void packet_buffer_init(PACKET_BUFFER_T* pb);
// use packet buffer utilities to parse existing data. No need to call destroy.
void packet_buffer_parse(PACKET_BUFFER_T* pb, unsigned char* data, int len);
void packet_buffer_destroy(PACKET_BUFFER_T* pb);
void packet_buffer_clear(PACKET_BUFFER_T* pb);

void packet_buffer_get_data(PACKET_BUFFER_T* pb, unsigned char** p_data, int* p_len);

void packet_buffer_push_uint8(PACKET_BUFFER_T* pb, unsigned char data);
void packet_buffer_push_uint16(PACKET_BUFFER_T* pb, unsigned short data);
void packet_buffer_push_uint32(PACKET_BUFFER_T* pb, unsigned int data);
void packet_buffer_set_uint8(PACKET_BUFFER_T* pb, int index, unsigned char data);
void packet_buffer_set_uint16(PACKET_BUFFER_T* pb, int index, unsigned short data);
void packet_buffer_set_uint32(PACKET_BUFFER_T* pb, int index, unsigned int data);

/* 1 for success, 0 for failure */
int packet_buffer_shift_uint8(PACKET_BUFFER_T* pb, unsigned char* p_data);
int packet_buffer_shift_uint16(PACKET_BUFFER_T* pb, unsigned short* p_data);
int packet_buffer_shift_uint32(PACKET_BUFFER_T* pb, unsigned int* p_data);
unsigned char packet_buffer_get_uint8(PACKET_BUFFER_T* pb, int index);
unsigned short packet_buffer_get_uint16(PACKET_BUFFER_T* pb, int index);
unsigned int packet_buffer_get_uint32(PACKET_BUFFER_T* pb, int index);

#endif // MISC_UTILS_H
